// led_manager.c
#include "led_manager.h"
#include <string.h>

static LedState led_list[MAX_LEDS];
static int led_count = 0;
static int flow_index = -1;
static unsigned long last_flow_time = 0;
static unsigned long flow_interval = 300;
static bool flow_forward = true;
static uint8_t locked_pins[MAX_LOCKED];
static int locked_count = 0;

static DigitalWriteFn write;
static MillisFn millisFn;
static PwmSetupFn pwmSetup;
static PwmWriteFn pwmWrite;
static PwmDetachFn pwmDetach;

void led_manager_init(DigitalWriteFn w, MillisFn m, PwmSetupFn s, PwmWriteFn pw, PwmDetachFn d) {
    write = w;
    millisFn = m;
    pwmSetup = s;
    pwmWrite = pw;
    pwmDetach = d;
    led_count = 0;
    flow_index = -1;
    last_flow_time = 0;
    flow_interval = 300;
    flow_forward = true;
    locked_count = 0;
    memset(led_list, 0, sizeof(led_list));
    memset(locked_pins, 0, sizeof(locked_pins));
}

void led_manager_add(uint8_t pin) {
    if (led_count >= MAX_LEDS) return;
    led_list[led_count++] = (LedState){pin, LED_OFF, 500, 0, false, 0, 255, true};
    if (write) write(pin, true);
}

void led_manager_remove(uint8_t pin) {
    for (int i = 0; i < led_count; ++i) {
        if (led_list[i].pin == pin) {
            if (led_list[i].mode == LED_BREATH_PWM && pwmDetach) pwmDetach(led_list[i].pin);
            for (int j = i; j < led_count - 1; ++j) led_list[j] = led_list[j + 1];
            --led_count;
            break;
        }
    }
}

void led_manager_clean_all_mode(void) {
    for (int i = 0; i < led_count; ++i) {
        led_list[i].mode = LED_OFF;
        if (write) write(led_list[i].pin, true);
    }
}

static void apply_mode(LedState* led, LedMode mode, unsigned long interval,uint8_t channel) {
    if (led->mode == LED_BREATH_PWM && pwmDetach) pwmDetach(led->pin);
    led->mode = mode;
    led->interval = interval;
    led->lastToggle = millisFn();
    led->brightness = 0;
    led->breathingUp = true;
    led->pwmChannel = channel;

    if (mode == LED_ON) {
        if (write) write(led->pin, false);
        led->isOn = true;
    } else if (mode == LED_OFF) {
        if (write) write(led->pin, true);
        led->isOn = false;
    } else if (mode == LED_BREATH_PWM && pwmSetup) {
        pwmSetup(led->pin, channel);
    }
}

void led_manager_set_mode(LedConfig config) {
    for (int i = 0; i < led_count; ++i) {
        if (led_list[i].pin == config.pin) {
            if (!config.privileged && led_manager_is_locked(config.pin)) return;
            apply_mode(&led_list[i], config.mode, config.interval,config.pwm_channel);
            return;
        }
    }
}


LedMode led_manager_get_mode(uint8_t pin) {
    for (int i = 0; i < led_count; ++i) {
        if (led_list[i].pin == pin) return led_list[i].mode;
    }
    return LED_OFF;
}

void led_manager_set_flow_interval(unsigned long interval) {
    flow_interval = interval;
}

void led_manager_set_flow_direction(bool forward) {
    flow_forward = forward;
}

void led_manager_change(uint8_t pin) {
    LedConfig config = {
        .pin = pin,
        .mode = LED_CHANGE
    };
    led_manager_set_mode(config);
    for (int i = 0; i < led_count; ++i) {
        if (led_list[i].pin == pin) {
            led_list[i].breathingUp = !led_list[i].breathingUp;
            if (write) write(pin, led_list[i].breathingUp);
            return;
        }
    }
}

bool led_manager_is_locked(uint8_t pin) {
    for (int i = 0; i < locked_count; ++i) {
        if (locked_pins[i] == pin) return true;
    }
    return false;
}

void led_manager_lock_pins(const uint8_t* pins, size_t count) {
    for (size_t i = 0; i < count; ++i) {
        if (!led_manager_is_locked(pins[i]) && locked_count < MAX_LOCKED) {
            locked_pins[locked_count++] = pins[i];
        }
    }
}

void led_manager_unlock_pins(const uint8_t* pins, size_t count) {
    for (size_t i = 0; i < count; ++i) {
        for (int j = 0; j < locked_count; ++j) {
            if (locked_pins[j] == pins[i]) {
                for (int k = j; k < locked_count - 1; ++k) locked_pins[k] = locked_pins[k + 1];
                --locked_count;
                break;
            }
        }
    }
}

void led_manager_update(void) {
    unsigned long now = millisFn();
    bool has_flow = false;
    int total_flow = 0;

    for (int i = 0; i < led_count; ++i) {
        LedState* led = &led_list[i];
        switch (led->mode) {
            case LED_BLINK:
                if (now - led->lastToggle >= led->interval) {
                    led->lastToggle = now;
                    led->isOn = !led->isOn;
                    if (write) write(led->pin, led->isOn ? false : true);
                }
                break;
            case LED_BREATH_PWM:
                if (now - led->lastToggle >= led->interval / 255) {
                    led->lastToggle = now;
                    if (led->breathingUp) {
                        led->brightness++;
                        if (led->brightness >= 255) led->breathingUp = false;
                    } else {
                        led->brightness--;
                        if (led->brightness <= 0) led->breathingUp = true;
                    }
                    if (pwmWrite) pwmWrite(led->pin, led->brightness);
                }
                break;
            case LED_FLOW:
                has_flow = true;
                total_flow++;
                break;
            default:
                break;
        }
    }

    if (has_flow && now - last_flow_time >= flow_interval) {
        last_flow_time = now;
        int flow_pos = 0;
        if (flow_index < 0) flow_index = flow_forward ? total_flow - 1 : 0;
        int next_flow = (flow_index + (flow_forward ? 1 : -1) + total_flow) % total_flow;

        for (int i = 0; i < led_count; ++i) {
            if (led_list[i].mode == LED_FLOW) {
                if (flow_pos == flow_index) {
                    write(led_list[i].pin, true);
                } else if (flow_pos == next_flow) {
                    write(led_list[i].pin, false);
                } else {
                    write(led_list[i].pin, true);
                }
                flow_pos++;
            }
        }
        flow_index = next_flow;
    }
}

LedState* led_manager_get_all(void) {
    return led_list;
}

int led_manager_get_count(void) {
    return led_count;
}
